package com.gitee.sparta.core.cluster.nio;

import cn.hutool.core.collection.CollUtil;
import com.gitee.sparta.common.SpartaConstant;
import com.gitee.sparta.common.helpers.JsonHelper;
import com.gitee.sparta.core.bean.cluster.ClusterProtocol;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * 读取，并处理 TCP 流式数据切分的问题，转换成协议对象
 * <p>
 * 拆包写，拆包读
 *
 * @author zyred
 * @since v 0.0.2
 * @deprecated 弃用 nio 作为通讯组件
 */
@Slf4j
@Deprecated
public class HandlerReadWriteHelper {

    /**
     * 4 + 1 + 4 + 4
     * 总长度占位 + code + sid + leaderSid
     */
    private static final int BASE_LEN = 13;

    /**
     * 从 channel 中读取所有的数据出来
     *
     * @param self          channel
     * @return              protocol
     * @throws IOException  exception
     */
    @SuppressWarnings("unchecked")
    public static List<ClusterProtocol> readProtocol (SocketChannel self) throws IOException {
        ByteBuffer buffer = dilatationRead(self);
        // 无数据
        if (buffer.capacity() == 0) {
            return Collections.emptyList();
        }

        List<ClusterProtocol> result = new ArrayList<>();
        while (buffer.hasRemaining()) {
            ClusterProtocol protocol = new ClusterProtocol();
            int len = buffer.getInt();
            byte code = buffer.get();
            int sid = buffer.getInt();
            int leaderSid = buffer.getInt();
            protocol.setCmd(code);
            protocol.setLeaderSid(leaderSid);
            protocol.setServerId(sid);
            if (len != BASE_LEN) {
                int topicLen = len - BASE_LEN;
                byte[] topicBytes = new byte[topicLen];
                buffer.get(topicBytes);
                protocol.setTopics(JsonHelper.readValue(topicBytes, Collection.class));
            }
            result.add(protocol);
        }
        return result;
    }


    /**
     * 写协议
     *
     * @param protocol      协议
     * @param cli           channel
     * @throws IOException  exception
     */
    public static void writeProtocol (ClusterProtocol protocol, SocketChannel cli) throws IOException {
        int len = HandlerReadWriteHelper.BASE_LEN;
        boolean isNotEmpty = CollUtil.isNotEmpty(protocol.getTopics());
        String topics = null;
        if (isNotEmpty) {
            topics = JsonHelper.getJsonString(protocol.getTopics());
            len += topics.length();
        }

        ByteBuffer buffer = ByteBuffer.allocate(len);
        buffer.putInt(len);
        buffer.put(protocol.getCmd());
        buffer.putInt(protocol.getServerId());
        buffer.putInt(protocol.getLeaderSid());

        if (isNotEmpty) {
            ByteBuffer encode = StandardCharsets.UTF_8.encode(topics);
            buffer.put(encode);
        }
        buffer.flip();
        while (buffer.hasRemaining()) {
            cli.write(buffer);
        }
    }

    /**
     * 扩容读取
     *
     * @param self          channel
     * @return              byte buffer
     * @throws IOException  exception
     */
    private static ByteBuffer dilatationRead (SocketChannel self) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(SpartaConstant.CAPACITY);
        int read = self.read(buffer);

        // 通道关闭 | 读无数据
        if (read <= 0) {
            return ByteBuffer.allocate(0);
        }

        // 读满了，剩余还有
        if (buffer.limit() == buffer.position()) {
            buffer.flip();
            while (read > 0) {
                ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
                newBuffer.put(buffer);
                buffer.clear();
                read = self.read(buffer);
                newBuffer.flip();
                buffer = newBuffer;
            }
        } else {
            buffer.flip();
        }
        return buffer;
    }
}
